package com.lhkj.ct.meta.modules.gauge.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.lhkj.ct.base.mybatis.extension.service.impl.BatchHandleServiceImpl;
import com.lhkj.ct.base.utils.Validate;
import com.lhkj.ct.meta.modules.gauge.common.enums.ConfigProcess;
import com.lhkj.ct.meta.modules.gauge.common.enums.GaugeType;
import com.lhkj.ct.meta.modules.gauge.mapper.GaugeQuestionMapper;
import com.lhkj.ct.meta.modules.gauge.mapper.QnrMapper;
import com.lhkj.ct.meta.modules.gauge.mapper.ScaleMapper;
import com.lhkj.ct.meta.modules.gauge.model.dto.QuestionsDto;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblGaugeOption;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblGaugeQuestion;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblQnr;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblScale;
import com.lhkj.ct.meta.modules.gauge.service.GaugeOptionService;
import com.lhkj.ct.meta.modules.gauge.service.GaugeQuestionService;
import javafx.util.Pair;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author jobob
 * @since 2023-10-08
 */
@Service
public class GaugeQuestionServiceImpl extends BatchHandleServiceImpl<GaugeQuestionMapper, TblGaugeQuestion> implements GaugeQuestionService {

    @Resource
    private GaugeOptionService optionService;

//    @Resource
//    private QutLogicService qutLogicService;

    @Resource
    private ScaleMapper scaleMapper;

    @Resource
    private QnrMapper qnrMapper;

    @Override
    public List<TblGaugeQuestion> selectQutWithOptWithLogicByGaugeId(String gaugeId, GaugeType type) {
        return getBaseMapper().selectQutWithOptWithLogicByGaugeId(gaugeId, type);
    }

    @Override
    public List<TblGaugeQuestion> selectQutByGaugeId(String gaugeId, GaugeType type) {
        return getBaseMapper().selectList(
                new LambdaQueryWrapper<TblGaugeQuestion>()
                        .select(TblGaugeQuestion::getQutId, TblGaugeQuestion::getQutSort, TblGaugeQuestion::getQutContent)
                        .eq(TblGaugeQuestion::getGaugeId, gaugeId)
                        .eq(TblGaugeQuestion::getGaugeType, type)
        );
    }

    @Override
    @Transactional
    public void saveByGauge(String gaugeId, GaugeType type, QuestionsDto questionsDto) {
        // 校验量表或问卷
        if (type == GaugeType.SCALE) {
            TblScale scale = scaleMapper.selectById(gaugeId);
            Validate.notNull(scale, "测验量表不存在或已删除！");
            scale.setProgress(ConfigProcess.BASIC.equals(scale.getProgress()) ? ConfigProcess.QUESTION : scale.getProgress());
            scale.setQuestionJson(questionsDto.getQuestionJson());
            scaleMapper.updateById(scale);
        }
        if (type == GaugeType.QUESTIONNAIRE) {
            TblQnr qnr = qnrMapper.selectById(gaugeId);
            Validate.notNull(qnr, "调查问卷不存在或已删除！");
            Validate.validState(qnr.getStatus() != 2, "调查问卷已分配，不支持修改题目！");
            qnr.setQuestionJson(questionsDto.getQuestionJson());
            qnrMapper.updateById(qnr);
        }
        // 前端提交的题目数据
        List<TblGaugeQuestion> questions = questionsDto.getQuestions();
        // 查询旧的题目、选项、逻辑数据
        List<TblGaugeQuestion> oldQuestions = this.selectQutWithOptWithLogicByGaugeId(gaugeId, type);
        // 收集旧的题目id，用来判断是否有删除的题目
        List<String> deleteQuestionIds = oldQuestions.stream().map(TblGaugeQuestion::getQutId).collect(Collectors.toList());
        // 数据库题目列表按照题目id进行合并
        Map<String, TblGaugeQuestion> questionMap = oldQuestions.stream()
                .collect(Collectors.toMap
                        (TblGaugeQuestion::getQutId, each -> each, (value1, value2) -> value1));
        // 初始化待新增的题目
        List<TblGaugeQuestion> newQuestions = new ArrayList<>();
        // 初始化待更新的题目
        List<Pair<TblGaugeQuestion, TblGaugeQuestion>> updateQuestionList = new ArrayList<>();
        // 处理排序
        for (int i = 0; i < questions.size(); i++) {
            // 题目
            TblGaugeQuestion question = questions.get(i);
            question.setName("NO" + (i + 1));
            question.setQutSort(i + 1);
            question.setGaugeId(gaugeId);
            question.setGaugeType(type);
            for (int j = 0; j < question.getOptions().size(); j++) {
                TblGaugeOption option = question.getOptions().get(j);
                option.setOptSort(j + 1);
            }
            // 新增
            if (StringUtils.isBlank(question.getQutId())) {
                // 防止新题目设置选项id
                question.getOptions().forEach(option -> {option.setOptId(null);});
                newQuestions.add(question);
                continue;
            }
            // 移除前端提交的题目id，剩余的则是需要删除的题目id
            deleteQuestionIds.remove(question.getQutId());
            // 无变化
            if (oldQuestions.remove(question)) {
                continue;
            }
            // 修改
            if (questionMap.containsKey(question.getQutId())) {
                updateQuestionList.add(new Pair<>(question, questionMap.get(question.getQutId())));
//                this.update(question, questionMap.get(question.getQutId()));
            }
        }
        // 批量新增题目
        super.saveBatch(newQuestions, 500);
        // vid对应题目id
        final Map<String, String> vIdQuestionIdMap = questionsDto.getQuestions()
                .stream()
                .filter(question -> StringUtils.isNotBlank(question.getVId()))
                .collect(Collectors.toMap(TblGaugeQuestion::getVId, TblGaugeQuestion::getQutId));
        // 给新的题目，选项设置逻辑
        List<TblGaugeOption> newOptions = newQuestions
                .stream()
                // 给选项设置题目id
                .peek(question -> question.getOptions().forEach(option -> option.setQuestionId(question.getQutId())))
                // 得到所有选项流
                .flatMap(question -> question.getOptions().stream())
                // 过滤得到有设置题目逻辑的选项
                .filter(option -> StringUtils.isNotBlank(option.getOptLogic()))
                // 设置选项逻辑
                .peek(option -> {
                    option.setOptLogic(vIdQuestionIdMap.getOrDefault(option.getOptLogic(), option.getOptLogic()));
                }).collect(Collectors.toList());
        optionService.saveBatch(newOptions);
        // 需要修改的题目
        updateQuestionList
                .stream()
                // 过滤出有选项的题目
                .filter(pair -> CollectionUtils.isNotEmpty(pair.getKey().getOptions()))
                // 设置选项逻辑
                .forEach(pair -> {
                    pair.getKey().getOptions().forEach(option -> option.setOptLogic(vIdQuestionIdMap.getOrDefault(option.getOptLogic(), option.getOptLogic())));
                    this.update(pair.getKey(), pair.getValue());
                });
        if (CollectionUtil.isNotEmpty(deleteQuestionIds)) {
            getBaseMapper().deleteBatchIds(deleteQuestionIds);
        }

    }

    @Override
    public boolean saveBatch(Collection<TblGaugeQuestion> entityList) {
        boolean res = super.saveBatch(entityList, 500);
        if (!res) return false;
        List<TblGaugeOption> options = entityList.stream()
                .filter(q -> CollectionUtils.isNotEmpty(q.getOptions()))
                .flatMap(q -> q.getOptions().stream().peek(o -> o.setQuestionId(q.getQutId())))
                .collect(Collectors.toList());
        return options.isEmpty() && optionService.saveBatch(options);
    }

    /**
     * 更新题目
     *
     * @param source 前端提交的题目数据
     * @param target 数据库的题目数据
     */
    private void update(TblGaugeQuestion source, TblGaugeQuestion target) {
        if (!source.compare(target)) {
            updateById(source);
        }
        List<String> optIds = target.getOptions().stream().map(TblGaugeOption::getOptId).collect(Collectors.toList());

        Map<String, TblGaugeOption> optionMap = target.getOptions().stream().collect(
                Collectors.toMap
                        (TblGaugeOption::getOptId, each -> each, (value1, value2) -> value1)
        );
        List<TblGaugeOption> newOptions = new ArrayList<>();
        for (TblGaugeOption option : source.getOptions()) {
            option.setQuestionId(source.getQutId());
            // 新增
            if (StringUtils.isBlank(option.getOptId())) {
                newOptions.add(option);
            }
            optIds.remove(option.getOptId());
            // 无变化
            if (target.getOptions().remove(option)) {
                continue;
            }
            // 有更新
            if (optionMap.containsKey(option.getOptId())) {
                optionService.updateById(option);
            }
        }
        optionService.saveBatch(newOptions);
        if (CollectionUtils.isNotEmpty(optIds)) {
            optionService.removeByIds(optIds);
        }
    }
}
